home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 June: Reference Library / Dev.CD Jun 96 RL / Dev.CD Jun 96 RL.toast / Technical Documentation / develop / develop Issue 24 / develop Issue 24 code / Scriptable Database 1.0a15 / Database / AbstractRecord.cp next >
Encoding:
Text File  |  1996-04-25  |  14.7 KB  |  391 lines  |  [TEXT/CWIE]

  1. //================================================================================
  2. // Greg Anderson
  3. // db+
  4. //
  5. // Abstract base class for cursors
  6. // 17 May 1994
  7. // 31 Dec 1994
  8. //================================================================================
  9.  
  10. #include "AbstractRecord.h"
  11.  
  12. #include "DatabaseDocument.h"
  13. #include "GroupControlObject.h"
  14.  
  15. //
  16. // Needed to use templates of these classes
  17. //
  18. #include "DBRecord.h"
  19. #include "DBElement.h"
  20. #include "DBProperty.h"
  21. #include "DataRecord.h"
  22.  
  23. #include "Exceptions.h"
  24.  
  25. //--------------------------------------------------------------------------------
  26. // TAbstractRecord::TAbstractRecord
  27. //--------------------------------------------------------------------------------
  28. TAbstractRecord::TAbstractRecord(TDatabaseDocument* doc, long recordIndex)
  29. {
  30.     REQUIREVALIDPOINTER(doc);
  31.     
  32.     fDocument = doc;
  33.     fGroupControlObject = nil;
  34.     fRecordIndex = recordIndex;
  35.     fChangeImage = nil;
  36.     
  37.     this->CacheGroupControlObject();
  38. } // TAbstractRecord::TAbstractRecord
  39.  
  40. //--------------------------------------------------------------------------------
  41. // TAbstractRecord::CacheGroupControlObject
  42. //--------------------------------------------------------------------------------
  43. void TAbstractRecord::CacheGroupControlObject()
  44. {
  45.     fGroupControlObject = fDocument->GetGroupControlObject(fRecordIndex);
  46. } // TAbstractRecord::CacheGroupControlObject
  47.  
  48. //--------------------------------------------------------------------------------
  49. // TAbstractRecord::~TAbstractRecord
  50. //--------------------------------------------------------------------------------
  51. TAbstractRecord::~TAbstractRecord()
  52. {
  53.     //
  54.     // If the group control object was set to nil,
  55.     // that means that this object has been removed
  56.     // from its group control object, and is now
  57.     // more or less unusable (this happens when a
  58.     // free record is pushed onto the free stack
  59.     // and the same record index is reused for something
  60.     // else--the old free record cursor is left
  61.     // dangling until all of its references are
  62.     // removed, at which time it is deleted and its
  63.     // changes are _not_ discarded or committed, because
  64.     // that has already been done.)
  65.     //
  66.     if(this->HasGroupControlObject())
  67.         this->DiscardChanges(this->Transaction());
  68.     else
  69.         ASSERT(fChangeImage == nil);
  70. } // TAbstractRecord::~TAbstractRecord
  71.  
  72. //--------------------------------------------------------------------------------
  73. // TAbstractRecord::ObjectsKeySpace
  74. //--------------------------------------------------------------------------------
  75. Int64 TAbstractRecord::ObjectsKeySpace() const
  76. {
  77.     return DBDocument()->ObjectsKeySpace();
  78. } // TAbstractRecord::ObjectsKeySpace
  79.  
  80. //--------------------------------------------------------------------------------
  81. // TAbstractRecord::GetRecordCursor
  82. //--------------------------------------------------------------------------------
  83. AConst<TAbstractRecord> TAbstractRecord::GetRecordCursor(long recordIndex) const
  84. {
  85.     return this->DBDocument()->GetRecordCursor(recordIndex);
  86. } // TAbstractRecord::GetRecordCursor
  87.  
  88. //--------------------------------------------------------------------------------
  89. // TAbstractRecord::GetDBRecordCursor
  90. //--------------------------------------------------------------------------------
  91. AConst<TDBRecord> TAbstractRecord::GetDBRecordCursor(long recordIndex) const
  92. {
  93.     AConst<TDBRecord> dbCursor;
  94.     AConst<TAbstractRecord> cursor = this->GetRecordCursor(recordIndex);
  95.     
  96.     if(cursor.Exists())
  97.         dbCursor = AConst<TDBRecord>(cursor->AbstractDBRecord());
  98.     
  99.     return dbCursor;
  100. } // TAbstractRecord::GetDBRecordCursor
  101.  
  102. //--------------------------------------------------------------------------------
  103. // TAbstractRecord::GetDBElementCursor
  104. //--------------------------------------------------------------------------------
  105. AConst<TDBElement> TAbstractRecord::GetDBElementCursor(long recordIndex) const
  106. {
  107.     AConst<TDBElement> dbCursor;
  108.     AConst<TAbstractRecord> cursor = this->GetRecordCursor(recordIndex);
  109.     
  110.     if(cursor.Exists())
  111.         dbCursor = AConst<TDBElement>(cursor->DBElementRecord());
  112.     
  113.     return dbCursor;
  114. } // TAbstractRecord::GetDBElementCursor
  115.  
  116. //--------------------------------------------------------------------------------
  117. // TAbstractRecord::GetDBPropertyCursor
  118. //--------------------------------------------------------------------------------
  119. AConst<TDBProperty> TAbstractRecord::GetDBPropertyCursor(long recordIndex) const
  120. {
  121.     AConst<TDBProperty> dbCursor;
  122.     AConst<TAbstractRecord> cursor = this->GetRecordCursor(recordIndex);
  123.     
  124.     if(cursor.Exists())
  125.         dbCursor = AConst<TDBProperty>(cursor->DBPropertyRecord());
  126.     
  127.     return dbCursor;
  128. } // TAbstractRecord::GetDBPropertyCursor
  129.  
  130. //--------------------------------------------------------------------------------
  131. // TAbstractRecord::GetDataCursor
  132. //--------------------------------------------------------------------------------
  133. AConst<TDataRecord> TAbstractRecord::GetDataCursor(long recordIndex) const
  134. {
  135.     AConst<TDataRecord> dbCursor;
  136.     AConst<TAbstractRecord> cursor = this->GetRecordCursor(recordIndex);
  137.     
  138.     if(cursor.Exists())
  139.         dbCursor = AConst<TDataRecord>(cursor->DataRecord());
  140.     
  141.     return dbCursor;
  142. } // TAbstractRecord::GetDataCursor
  143.  
  144. //--------------------------------------------------------------------------------
  145. // TAbstractRecord::CommitChanges
  146. //--------------------------------------------------------------------------------
  147. void TAbstractRecord::CommitChanges(TTransaction* transaction)
  148. {
  149.     if(this->InTransaction(transaction) == false)
  150.         FailErr(eNotInTransaction);
  151.         
  152.     //
  153.     // If we have a change image and it has been touched...
  154.     //
  155.     if((fChangeImage != nil) && (fChangeImageUnchanged == false))
  156.     {
  157.         //
  158.         // Commit the changes back to the group control object
  159.         //
  160.         // n.b.    The group control object will compare the new
  161.         //        data with the old to try to avoid dirtying the
  162.         //        entire group unnecessarily.  We can avoid an
  163.         //        unnecessary call to memcmp if we know for certain
  164.         //        that the data has changed.  This information
  165.         //        is stored in fChangeImageMayHaveChangedBack.
  166.         //
  167.         this->GroupControlObject()->ChangeRecordData(this->RecordIndex(), fChangeImage, (fChangeImageMayHaveChangedBack == false));
  168.     }
  169.  
  170.     //
  171.     // Once we've committed our changes, discard
  172.     // the change image that's left over and push
  173.     // this record back on the free stack if it
  174.     // is free.
  175.     //
  176.     this->DiscardChanges(transaction);
  177. } // TAbstractRecord::CommitChanges
  178.  
  179. //--------------------------------------------------------------------------------
  180. // TAbstractRecord::DiscardChanges
  181. //--------------------------------------------------------------------------------
  182. void TAbstractRecord::DiscardChanges(TTransaction* transaction)
  183. {
  184.     if(this->InTransaction(transaction) == false)
  185.         FailErr(eNotInTransaction);
  186.         
  187.     if(fChangeImage != nil)
  188.     {
  189.         //
  190.         // Delete the change image object
  191.         //
  192.         delete [] fChangeImage;
  193.         fChangeImage = nil;
  194.     }
  195.     
  196.     this->PushRecordIfFree(transaction);
  197. } // TAbstractRecord::DiscardChanges
  198.  
  199. //--------------------------------------------------------------------------------
  200. // TAbstractRecord::GetRecordData
  201. //--------------------------------------------------------------------------------
  202. long TAbstractRecord::GetRecordData(TTransaction* transaction, long longwordNumber) const
  203. {
  204.     if((fChangeImage != nil) && (this->InTransaction(transaction)))
  205.         return fChangeImage[longwordNumber];
  206.     else
  207.         return this->GroupControlObject()->GetRecordWord(this->RecordIndex(), longwordNumber);
  208. } // TAbstractRecord::GetRecordData
  209.  
  210. //--------------------------------------------------------------------------------
  211. // TAbstractRecord::ChangeRecordData
  212. //--------------------------------------------------------------------------------
  213. void TAbstractRecord::ChangeRecordData(TTransaction* transaction, long longwordNumber, long newValue)
  214. {
  215.     if(this->InTransaction(transaction) == false)
  216.         FailErr(eNotInTransaction);
  217.     
  218.     //
  219.     // If we don't have a change image yet, then make one
  220.     //
  221.     if(fChangeImage == nil)
  222.     {
  223.         fChangeImage = this->GroupControlObject()->MakeRecordDataCopy(this->RecordIndex());
  224.         fChangeImageUnchanged = true;
  225.         fChangeImageMayHaveChangedBack = false;
  226.     }
  227.     
  228.     //
  229.     // Don't do anything at all unless the data has
  230.     // actually changed.
  231.     //
  232.     if(fChangeImage[longwordNumber] != newValue)
  233.     {
  234.         //
  235.         // Write the new word into the change image,
  236.         // then do some tests to see if the data has
  237.         // changed at all.
  238.         //
  239.         fChangeImage[longwordNumber] = newValue;
  240.         if(newValue == this->GroupControlObject()->GetRecordWord(this->RecordIndex(), longwordNumber))
  241.         {
  242.             fChangeImageMayHaveChangedBack = true;
  243.         }
  244.         else
  245.         {
  246.             fChangeImageUnchanged = false;
  247.             fChangeImageMayHaveChangedBack = false;
  248.         }
  249.     }
  250. } // TAbstractRecord::ChangeRecordData
  251.  
  252. //--------------------------------------------------------------------------------
  253. // TAbstractRecord::WriteThroughToTransaction
  254. //
  255. // This method is called only from
  256. // TGroupControlObject::WriteThroughToTransaction; see the comment there
  257. // for information about what this is for.
  258. //--------------------------------------------------------------------------------
  259. void TAbstractRecord::WriteThroughToTransaction(long longwordNumber, long theData, long theMask)
  260. {
  261.     //
  262.     // The group control object has taken care of its data; we only
  263.     // need to update our change image.
  264.     //
  265.     if(fChangeImage != nil)
  266.     {
  267.         long currentWordValue = fChangeImage[longwordNumber];
  268.         fChangeImage[longwordNumber] = (currentWordValue & ~theMask) | (theData & theMask);
  269.     }
  270. } // TAbstractRecord::WriteThroughToTransaction
  271.  
  272. //--------------------------------------------------------------------------------
  273. // TAbstractRecord::CompareRecordData
  274. //--------------------------------------------------------------------------------
  275. CompareEnumeration TAbstractRecord::CompareRecordData(TTransaction* transaction, long dataType, long longwordNumber, long numberOfBytes, const TAbstractDataReference& compareWith) const
  276. {
  277.     const TConstDataReference recordDataReference(this->RecordDataReference(transaction, dataType, longwordNumber, numberOfBytes));
  278.  
  279.     return recordDataReference.Compare(compareWith);
  280. } // TAbstractRecord::CompareRecordData
  281.  
  282. //--------------------------------------------------------------------------------
  283. // TAbstractRecord::RecordDataContains
  284. //--------------------------------------------------------------------------------
  285. Boolean TAbstractRecord::RecordDataContains(TTransaction* transaction, long dataType, long longwordNumber, long numberOfBytes, const TAbstractDataReference& compareWith) const
  286. {
  287.     const TConstDataReference recordDataReference(this->RecordDataReference(transaction, dataType, longwordNumber, numberOfBytes));
  288.  
  289.     return recordDataReference.Contains(compareWith);
  290. } // TAbstractRecord::RecordDataContains
  291.  
  292. //--------------------------------------------------------------------------------
  293. // TAbstractRecord::ThisRecordIsFree
  294. //--------------------------------------------------------------------------------
  295. Boolean TAbstractRecord::ThisRecordIsFree(TTransaction* transaction) const
  296. {
  297.     return ((this->GetRecordData(transaction, kFreeNodeIDByte) & kFreeRecordIDBits) == kFreeRecordIDBits);
  298. } // TAbstractRecord::ThisRecordIsFree
  299.  
  300. //--------------------------------------------------------------------------------
  301. // TAbstractRecord::RemoveReference
  302. //--------------------------------------------------------------------------------
  303. Boolean TAbstractRecord::RemoveReference() const
  304. {
  305.     Boolean noMoreReferences = TTransactionAwareObject::RemoveReference();
  306.     
  307.     //
  308.     // Usually, RemoveReference will not delete the item;
  309.     // however, if DisposeRecordIfUnreferenced was called
  310.     // it may defer deletion until the last reference
  311.     // actually goes away.  In that case, we will delete
  312.     // the record when the last reference is released.
  313.     //
  314.     // Perhaps we should move this functionality down
  315.     // to the base class
  316.     //
  317.     if((noMoreReferences == true) && (this->HasGroupControlObject() == false))
  318.     {
  319.         delete (TAbstractRecord*)this;
  320.     }
  321.     
  322.     return noMoreReferences;
  323. } // TAbstractRecord::RemoveReference
  324.  
  325. //--------------------------------------------------------------------------------
  326. // TAbstractRecord::DisposeRecordIfUnreferenced
  327. //--------------------------------------------------------------------------------
  328. void TAbstractRecord::DisposeRecordIfUnreferenced()
  329. {
  330.     //
  331.     // Abstract records are only disposed of by their group control
  332.     // object at the time when they are disassociated from the GCO.
  333.     // We need to signal that this record is no longer useful by
  334.     // setting the group control object to nil.  
  335.     //
  336.     // Inherited::DisposeRecordIfUnreferenced will dispose of
  337.     // this record if there are no references to it, but if there
  338.     // ARE references to it, then the last call to RemoveReference
  339.     // will delete this object.
  340.     //
  341.     ASSERT(fChangeImage == nil);
  342.     fGroupControlObject = nil;
  343.     TTransactionAwareObject::DisposeRecordIfUnreferenced();
  344. } // TAbstractRecord::DisposeRecordIfUnreferenced
  345.  
  346. //--------------------------------------------------------------------------------
  347. // TAbstractRecord::FreeOwnedData
  348. //--------------------------------------------------------------------------------
  349. void TAbstractRecord::FreeOwnedData(TTransaction* /*transaction*/)
  350. {
  351. } // TAbstractRecord::FreeOwnedData
  352.  
  353. //--------------------------------------------------------------------------------
  354. // TAbstractRecord::FreeThisRecord
  355. //--------------------------------------------------------------------------------
  356. void TAbstractRecord::FreeThisRecord(TTransaction* transaction)
  357. {
  358.     if(this->ThisRecordIsFree(transaction) == false)
  359.     {
  360.         this->FreeOwnedData(transaction);
  361.         long previousFlags = this->GetRecordData(transaction, kFreeNodeIDByte);
  362.         this->ChangeRecordData(transaction, kFreeNodeIDByte, previousFlags | kFreeRecordIDBits);
  363.         this->ChangeRecordData(transaction, kFreeNodeLinkByte, kFreeRecordNotLinkedToTree);
  364.     }
  365. } // TAbstractRecord::FreeThisRecord
  366.  
  367. //--------------------------------------------------------------------------------
  368. // TAbstractRecord::PushRecordIfFree
  369. //--------------------------------------------------------------------------------
  370. void TAbstractRecord::PushRecordIfFree(TTransaction* transaction)
  371. {
  372.     if((this->ThisRecordIsFree(transaction)) && (this->GetRecordData(transaction, kFreeNodeLinkByte) == kFreeRecordNotLinkedToTree))
  373.     {
  374.         this->DBDocument()->PushFreeRecordOntoFreeList(this->RecordIndex());
  375.     }
  376. } // TAbstractRecord::PushRecordIfFree
  377.  
  378. //--------------------------------------------------------------------------------
  379. // TAbstractRecord::RecordDataReference
  380. //
  381. // Private--only used to compare data in the record
  382. //--------------------------------------------------------------------------------
  383. const TConstDataReference TAbstractRecord::RecordDataReference(TTransaction* transaction, long dataType, long longwordNumber, long numberOfBytes) const
  384. {
  385.     if((fChangeImage != nil) && (this->InTransaction(transaction)))
  386.         return TConstDataReference(dataType, (char*)&fChangeImage[longwordNumber], numberOfBytes);
  387.     else
  388.         return this->GroupControlObject()->RecordDataReference(this->RecordIndex(), dataType, longwordNumber, numberOfBytes);
  389. } // TAbstractRecord::RecordDataReference
  390.  
  391.